#pragma once

#include <sqlite_orm/sqlite_orm.h>

#include <algorithm>
#include <filesystem>
#include <map>
#include <memory>
#include <tuple>
#include <type_traits>
#include <unordered_map>
#include <vector>

#include "logger.hpp"
#include "spdlog/fmt/fmt.h"

namespace fs = std::filesystem;

namespace std {

template <>
struct hash<tuple<string, string>> {
    size_t operator()(const tuple<string, string> &t) const {
        return hash<string>{}(get<0>(t) + get<1>(t));
    }
};

template <>
struct hash<tuple<string, string, string>> {
    size_t operator()(const tuple<string, string, string> &t) const {
        return hash<string>{}(get<0>(t) + get<1>(t) + get<2>(t));
    }
};
}  // namespace std

namespace InoIDE {
using PathAndTable = std::tuple<std::string, std::string>;

// 基类接口
class StorageBase {
   public:
    virtual ~StorageBase() = default;

    virtual void pragma_journal_mode(sqlite_orm::journal_mode mode) {
        throw std::logic_error(
            "pragma_journal_mode not implemented for this storage type");
    }
    virtual std::optional<sqlite_orm::journal_mode> pragma_journal_mode() {
        throw std::logic_error(
            "get pragma_journal_mode not implemented for this storage type");
    }
    virtual void pragma_user_version(int version) {
        throw std::logic_error(
            "pragma_user_version not implemented for this storage type");
    }
    virtual std::optional<int> pragma_user_version() {
        throw std::logic_error(
            "get pragma_user_version not implemented for this storage type");
    }
};

// 派生的Storage包装类
template <typename StorageType>
class StorageWrapper : public StorageBase {
   public:
    StorageWrapper(std::shared_ptr<StorageType> storage) : storage_(storage) {}

    std::shared_ptr<StorageType> getPtr() { return storage_; }
    const std::shared_ptr<StorageType> getPtr() const { return storage_; }

    std::shared_ptr<StorageType> getActualPtr() { return storage_; }
    const std::shared_ptr<StorageType> getActualPtr() const { return storage_; }

    // Override pragma methods
    void pragma_journal_mode(sqlite_orm::journal_mode mode) override {
        storage_->pragma.journal_mode(mode);
    }

    std::optional<sqlite_orm::journal_mode> pragma_journal_mode() override {
        return storage_->pragma.journal_mode();
    }

    void pragma_user_version(int version) override {
        storage_->pragma.user_version(version);
    }

    std::optional<int> pragma_user_version() override {
        return storage_->pragma.user_version();
    }

   private:
    std::shared_ptr<StorageType> storage_;
};

// Storage 管理器 - 管理多个不同类型的 sqlite_orm storage
class StorageManager {
   private:
    StorageManager() = default;
    ~StorageManager() = default;

    // 禁止拷贝和移动
    StorageManager(const StorageManager &) = delete;
    StorageManager &operator=(const StorageManager &) = delete;
    StorageManager(StorageManager &&) = delete;
    StorageManager &operator=(StorageManager &&) = delete;

   public:
    // 添加一个storage，返回shared_ptr
    template <class StorageType>
    std::shared_ptr<StorageType> addStorage(const PathAndTable &key,
                                            StorageType &&storage) {
        using RawType = std::decay_t<StorageType>;
        std::lock_guard<std::mutex> lock(mutex_);

        auto storagePtr =
            std::make_shared<RawType>(std::forward<StorageType>(storage));
        auto wrapper = std::make_shared<StorageWrapper<RawType>>(storagePtr);

        storages_[key] = wrapper;
        LOG_INFO("new Storage add, path is {}, dbfile is {}", std::get<0>(key),
                 std::get<1>(key));

        return storagePtr;
    }

    // 根据工厂函数创建并添加storage，返回shared_ptr
    template <typename StorageType>
    std::shared_ptr<StorageType> createStorage(
        const PathAndTable &key, std::function<StorageType()> factory) {
        try {
            std::lock_guard<std::mutex> lock(mutex_);
            auto dbFile = fs::path(std::get<0>(key));
            if (!fs::exists(dbFile.parent_path())) {
                if (!fs::create_directories(dbFile.parent_path())) {
                    auto errmsg =
                        fmt::format("DB parent path creat failed, path is{} ",
                                    dbFile.parent_path().string());
                    throw(std::runtime_error(errmsg));
                }
            }
            auto storagePtr = std::make_shared<StorageType>(factory());
            storagePtr->sync_schema();
            storagePtr->pragma.journal_mode(sqlite_orm::journal_mode::WAL);
            storagePtr->open_forever();

            storagePtr->busy_handler([](int retry_count) -> bool {
                LOG_INFO("Thread ID: {} Waiting... Attempt {}",
                         std::this_thread::get_id(), retry_count);
                std::this_thread::sleep_for(std::chrono::milliseconds(100));
                return true;  // 继续等待
            });

            // 创建wrapper并存储
            auto wrapper =
                std::make_shared<StorageWrapper<StorageType>>(storagePtr);
            storages_[key] = wrapper;

            if (storagePtr == nullptr) {
                auto errmsg = fmt::format(
                    "new Storage creat failed, path is{}, dbfile is {}",
                    std::get<0>(key), std::get<1>(key));
                throw(std::runtime_error(errmsg));
            }
            LOG_INFO("new Storage creat, path is {}, dbfile is {}",
                     std::get<0>(key), std::get<1>(key));
            return storagePtr;
        } catch (const std::exception &e) {
            LOG_ERROR("new Storage creat failed, for {}", e.what());
            return nullptr;
        }
    }

    // 获取指定key的storage，返回shared_ptr
    template <typename StorageType>
    std::shared_ptr<StorageType> getStorage(const PathAndTable &key) {
        std::lock_guard<std::mutex> lock(mutex_);

        auto it = storages_.find(key);
        if (it == storages_.end()) {
            LOG_ERROR("Storage not found:", std::get<0>(key), std::get<1>(key));
            throw std::runtime_error("Storage not found: " + std::get<0>(key) +
                                     std::get<1>(key));
        }

        // 尝试转换为指定类型
        auto typedWrapper =
            std::dynamic_pointer_cast<StorageWrapper<StorageType>>(it->second);
        if (!typedWrapper) {
            LOG_ERROR("Type mismatch for storage:", std::get<0>(key),
                      std::get<1>(key));
            throw std::runtime_error("Type mismatch for storage: " +
                                     std::get<0>(key) + std::get<1>(key));
        }
        if (typedWrapper->getPtr() == nullptr) {
            LOG_ERROR("getStorage return nullptr");
        }
        return typedWrapper->getPtr();
    }

    void flushCache() {
        /*auto session = getSession();
        auto sqlite = ((soci::sqlite3_session_backend
        *)session->get_backend())->conn_; if (sqlite)
        {
            sqlite3_db_cacheflush(sqlite);
        }
        releaseSession(nullptr);*/
    }

    /// wal文件写回主数据库后关闭连接
    void saveAllCache(const PathAndTable &key) {
        for (const auto &[key, storage] : storages_) {
            storage->pragma_journal_mode(sqlite_orm::journal_mode::OFF);
            // storage->pragma_journal_mode(sqlite_orm::journal_mode::WAL);
        }
    }

    // 检查storage是否存在
    bool hasStorage(const PathAndTable &key) const {
        std::lock_guard<std::mutex> lock(mutex_);
        return storages_.find(key) != storages_.end();
    }

    // 移除storage
    bool removeStorage(const PathAndTable &key) {
        std::lock_guard<std::mutex> lock(mutex_);
        return storages_.erase(key) > 0;
    }

    // 获取所有storage的key
    std::vector<PathAndTable> getAllKeys() const {
        std::lock_guard<std::mutex> lock(mutex_);
        std::vector<PathAndTable> keys;
        keys.reserve(storages_.size());

        for (const auto &[key, _] : storages_) {
            keys.push_back(key);
        }

        return keys;
    }

    // 清空所有storage
    void clear() {
        std::lock_guard<std::mutex> lock(mutex_);
        storages_.clear();
    }

   private:
    mutable std::mutex mutex_;
    std::unordered_map<PathAndTable, std::shared_ptr<StorageBase>> storages_;

   private:
    friend class StorageManagerSingleton;
};

class StorageManagerSingleton {
   public:
    static StorageManager &getInstance() {
        static StorageManager instance;
        return instance;
    }

   private:
    StorageManagerSingleton() = default;
};

inline StorageManager &
StorageManInstance() {  // 或者用一个你喜欢的名字，比如 getSM()
    return StorageManagerSingleton::getInstance();
}

}  // namespace InoIDE